/**
 * 
 */
package com.newtribe.security.cert;

import java.io.StringReader;
import java.security.PublicKey;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.Calendar;
import java.util.List;

import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.DEREncodableVector;
import org.bouncycastle.asn1.DERIA5String;
import org.bouncycastle.asn1.DERObjectIdentifier;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.pkcs.CertificationRequestInfo;
import org.bouncycastle.asn1.x509.AccessDescription;
import org.bouncycastle.asn1.x509.AuthorityInformationAccess;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.CRLDistPoint;
import org.bouncycastle.asn1.x509.CertificatePolicies;
import org.bouncycastle.asn1.x509.DistributionPoint;
import org.bouncycastle.asn1.x509.DistributionPointName;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.PolicyInformation;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.asn1.x509.X509Name;
import org.bouncycastle.jce.PKCS10CertificationRequest;
import org.bouncycastle.jce.X509KeyUsage;
import org.bouncycastle.openssl.PEMReader;
import org.bouncycastle.x509.X509V3CertificateGenerator;
import org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure;
import org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

import com.newtribe.security.cert.config.CertConfig;
import com.newtribe.security.cert.config.DnName;
import com.newtribe.security.cert.config.ExtentionItem;
import com.newtribe.security.kmc.KeyPairManager;

/**
 * 
 * @author newtribe
 *
 */
public class SignerEngine {

	private WorkerInstance worker =null ;
	
	public  SignerEngine() {
		
	}

	public  SignerEngine(WorkerInstance instance) {
		this.worker =instance ;
		
	}


	public X509Certificate signCert(String templateName)throws Exception{
		return signCert(templateName ,null) ;

	}
	/**
	 * sign a certificate from a template .
	 * @param templateName
	 * @return
	 * @throws Exception
	 */
	
	
	public X509Certificate signCert(String templateName ,String p10)throws Exception{
		if(templateName ==null)
			return null ;
		 String[] path={templateName
					
		 }; 
		 ApplicationContext context = new FileSystemXmlApplicationContext (path);

		 CertConfig config = (CertConfig)context.getBean("certconfig");
		 
		 return signCert(config ,p10);
		
	}
	public X509Certificate signCert( CertConfig config ,String p10)throws Exception{

	 Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
	 //process p10 

	 ////////////////////////
	 //begin process sign certificate by bc
	    X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
	    CAConfig caConfig =worker.getCaConfig();
	    KeyPairManager kp =new KeyPairManager(caConfig.getKmcConfig());
	    
	    
	    PublicKey userPublicKey =null ;
	    X509Name subjectDn =null ;
	    
	    if (p10 !=null) {
	    	//p10 = Base64.decode(p10);

	    	 //  PKCS10CertificationRequest pkcr = new PKCS10CertificationRequest(p10);
	    	StringReader reader =new StringReader(p10) ;
	    	PEMReader pemReader = new PEMReader( reader); 
	    	
	    	PKCS10CertificationRequest pkcr =(PKCS10CertificationRequest)pemReader.readObject(); 
	    	     CertificationRequestInfo cri = pkcr.getCertificationRequestInfo();
	    	     userPublicKey =pkcr.getPublicKey();
	    	     subjectDn=cri.getSubject();
	    	     
	    }else {
	    	userPublicKey =kp.generatePrikey(config.getKeygenAlgorithm(),config.getKeysize());
	    	subjectDn =config.getSubjectDN() ;
	    }
	    certGen.setPublicKey(userPublicKey);
	    //use configed issuedn .
	    DnName issuerDn = new DnName( kp.getCertificate(caConfig.getCertAlias()).getIssuerDN().toString()  );
	    certGen.setSubjectDN(subjectDn);
	   
	    certGen.setIssuerDN(issuerDn);

	    // expire
	    
	    Calendar calObj = Calendar.getInstance(); 
	 
	    certGen.setNotBefore(calObj.getTime());
	    calObj.add(Calendar.MONTH, config.getExpiresMonth()) ;
	   
	    certGen.setNotAfter(calObj.getTime());//
	    
	    certGen.setSerialNumber(config.getSerialNumber().getId());
	    
	    certGen.setSignatureAlgorithm(config.getSignatureAlgorithm());
	    
	    
	    //process extention according x509 v3 version 
	    
		BasicConstraints bc = new BasicConstraints(false); //false :end entity  ;    true is a ca authority
		certGen.addExtension(X509Extensions.BasicConstraints.getId(), true, bc);
		
	     SubjectKeyIdentifierStructure struct =new SubjectKeyIdentifierStructure(userPublicKey) ;
	     certGen.addExtension(X509Extensions.SubjectKeyIdentifier, false,struct);
	    
	     
		 PublicKey issuePublicKey =kp.getPubkey(caConfig.getSignkeyAlias()) ;
	     AuthorityKeyIdentifierStructure structIssue =new AuthorityKeyIdentifierStructure(issuePublicKey) ;	     
	     certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false,structIssue);
	     

	    List<ExtentionItem> extensions= config.getExtension() ;
	    
	    for( int i = 0 ;i<extensions.size() ;i ++) {

	    	ExtentionItem item =extensions.get(i) ;
	    	String enname =item.getEnName();
	    	if ("xxxxxxxxxxxxxxxx".equals(enname)) {
	
	    	}else if ("KeyUsage" .endsWith(enname)) {

	    	int keyusage = X509KeyUsage.keyCertSign |
	    					X509KeyUsage.keyEncipherment ;
	    	X509KeyUsage ku = new X509KeyUsage(keyusage);
	    	certGen.addExtension(X509Extensions.KeyUsage.getId(), true, ku);

	    	}else if ("SubjectAlternativeName" .equals(enname)) {
	    	    GeneralName gn = new GeneralName(GeneralName.iPAddress, new
	    	    		DERIA5String(item.getContent()));
	    	    
	    	    GeneralName gnDns = new GeneralName(GeneralName.dNSName, new
	    	    		DERIA5String("www.fuwei.com"));
//	    	    create a SubjectAlternativeName extension value
	    	    GeneralNames  subjectAltNames = new GeneralNames(
	    	    		gn);
	    	    
	    	    certGen.addExtension(X509Extensions.SubjectAlternativeName.getId(), true, subjectAltNames);
	    		
	    		
	    	}else if ("AuthorityInfoAccess" .equals(enname)) {
	            DEREncodableVector list = new DEREncodableVector(); 
	            // AIA extension for CA Issuers 
	            list.add(new AccessDescription(AccessDescription.id_ad_caIssuers, 
	                                           new GeneralName(GeneralName.uniformResourceIdentifier, 
	                                                           new DERIA5String(item.getContent())))); 
	            // AIA extension for OCSP access method. 
	            list.add(new AccessDescription(AccessDescription.id_ad_ocsp, 
	                                           new GeneralName(GeneralName.uniformResourceIdentifier, 
	                                                           new DERIA5String(item.getContent1())))); 
	            
	            certGen.addExtension(
	    		X509Extensions.AuthorityInfoAccess,false,new AuthorityInformationAccess(new DERSequence(list)));  
	            
	    		
	    		
	    	}else if ("CRL" .equals(enname)) {
	            
	            DistributionPoint point[] = new DistributionPoint[1];
	            DistributionPointName name  =new DistributionPointName(DistributionPointName.FULL_NAME, 
	            		new GeneralName(GeneralName.uniformResourceIdentifier,item.getContent()));       
	           point[0] = new DistributionPoint(name,null,null);
	           CRLDistPoint crlPoint = new CRLDistPoint(point);
	            certGen.addExtension(X509Extensions.CRLDistributionPoints,false,crlPoint);
	    		
	    		
	    	}else if ("KeyUsage" .equals(enname)) {
	    		
	    		
	    	}
	    	else if ("KeyUsage" .equals(enname)) {
	    		
	    	}


	    }


//	     
//	     String policyId ="0.3.3.3.6.77" ;
//	     if (policyId != null) {
//	        // CertificatePolicies cp = new CertificatePolicies(policyId);
//	         PolicyInformation pi = new PolicyInformation(new DERObjectIdentifier(  policyId));   
//	         DERSequence seq = new DERSequence(pi); 
//	         certGen.addExtension(X509Extensions.CertificatePolicies.getId(), false, seq);
//	     }
	     
	    
        
        
        
        
	  /////////////////  end extend

	    X509Certificate cert = certGen.generate(kp.getPrikey(caConfig.getSignkeyAlias()));
		 return cert;
	 
		
		
	}

}
